#ifndef VECTOR2D_HPP
#define VECTOR2D_HPP

#include "Utils.hpp"

/**
 * @file Vector2D.hpp
 * @brief 提供二维向量
 * @author Creeper
 * @date 2021-03-05 (Modified: 2021-07-21)
 * @version V1.1
 *
 * @note
 * 目前提供了以下内容
 * -# 向量（二元组）
 **/

typedef int Vector2DType;

template <typename value_type>
struct Vec2
{
public:
	value_type x, y;

	Vec2() = default;
	constexpr Vec2(const value_type &_x, const value_type &_y) : x(_x), y(_y) {}
    constexpr Vec2(const value_type &tar) : x(tar.first), y(tar.second) {}

	inline Vec2 operator+() const; ///< 向量取正
	inline Vec2 operator-() const; ///< 向量取反

	inline Vec2 operator+(const Vec2 &tar) const;	///< 向量加法
	inline Vec2 operator-(const Vec2 &tar) const;	///< 向量减法
	inline Vec2 operator*(const double &tar) const; ///< 向量缩放
	inline Vec2 operator/(const double &tar) const; ///< 向量缩放

	inline value_type operator*(const Vec2 &tar) const; ///< 向量点乘
	inline value_type operator&(const Vec2 &tar) const; ///< 向量叉乘

	inline Vec2 &operator+=(const Vec2 &tar);	///< 向量加法
	inline Vec2 &operator-=(const Vec2 &tar);	///< 向量减法
	inline Vec2 &operator*=(const double &tar); ///< 向量缩放
	inline Vec2 &operator/=(const double &tar); ///< 向量缩放

	inline bool operator<(const Vec2 &tar) const;  ///< 向量比较，支持嵌套
	inline bool operator<=(const Vec2 &tar) const; ///< 向量比较，支持嵌套
	inline bool operator>(const Vec2 &tar) const;  ///< 向量比较，支持嵌套
	inline bool operator>=(const Vec2 &tar) const; ///< 向量比较，支持嵌套

	inline value_type getNorm() const;	   ///< 其实是模长的平方，但是想不到什么变量名了
	inline value_type getDistance() const; ///< 距离
	inline Vec2 fabs() const;			   ///< 向量每个分量取绝对值

	inline Vec2 getE() const; ///< 返回单位向量
};

typedef Vec2<Vector2DType> Vector2D;
typedef Vec2<int> Vec2i;
typedef Vec2<Vector2D> TwoPoints;

template <typename value_type>
inline value_type getNorm(const Vec2<value_type> &tar);
template <typename value_type>
inline value_type getDistance(const Vec2<value_type> &tar);
template <typename value_type>
inline Vec2<value_type> fabs(const Vec2<value_type> &tar);

template <typename value_type>
std::string to_string(const Vec2<value_type> &tar);

template <typename value_type>
std::ostream &operator<<(std::ostream &ost, const Vec2<value_type> &tar);

template <typename value_type>
std::istream &operator>>(std::istream &ist, Vec2<value_type> &tar);

template <typename T>
inline bool isInRange(const T &, const T &, const T &);

template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator+() const
{
	return *this;
}

template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator-() const
{
	return {-x, -y};
}

template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator+(const Vec2 &tar) const
{
	return {x + tar.x, y + tar.y};
}

template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator-(const Vec2 &tar) const
{
	return {x - tar.x, y - tar.y};
}

template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator*(const double &tar) const
{
	return {x * tar, y * tar};
}
template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::operator/(const double &tar) const
{
	return {x / tar, y / tar};
}

template <typename value_type>
inline Vec2<value_type> &Vec2<value_type>::operator+=(const Vec2 &tar)
{
	return *this = *this + tar;
}
template <typename value_type>
inline Vec2<value_type> &Vec2<value_type>::operator-=(const Vec2 &tar)
{
	return *this = *this - tar;
}
template <typename value_type>
inline Vec2<value_type> &Vec2<value_type>::operator*=(const double &tar)
{
	return *this = *this * tar;
}
template <typename value_type>
inline Vec2<value_type> &Vec2<value_type>::operator/=(const double &tar)
{
	return *this = *this / tar;
}

template <typename value_type>
inline value_type Vec2<value_type>::operator*(const Vec2 &tar) const
{
	return x * tar.x + y * tar.y;
}
template <typename value_type>
inline value_type Vec2<value_type>::operator&(const Vec2 &tar) const
{
	return x * tar.y - y * tar.x;
}

template <typename value_type>
inline bool Vec2<value_type>::operator<(const Vec2 &tar) const
{
	return x < tar.x && y < tar.y;
}
template <typename value_type>
inline bool Vec2<value_type>::operator<=(const Vec2 &tar) const
{
	return x <= tar.x && y <= tar.y;
}
template <typename value_type>
inline bool Vec2<value_type>::operator>(const Vec2 &tar) const
{
	return x > tar.x && y > tar.y;
}
template <typename value_type>
inline bool Vec2<value_type>::operator>=(const Vec2 &tar) const
{
	return x >= tar.x && y >= tar.y;
}

template <typename value_type>
inline value_type Vec2<value_type>::getNorm() const
{
	return x * x + y * y;
}
template <typename value_type>
inline value_type Vec2<value_type>::getDistance() const
{
	return std::sqrt(getNorm());
}
template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::fabs() const
{
	return {std::fabs(x), std::fabs(y)};
}
template <typename value_type>
inline Vec2<value_type> Vec2<value_type>::getE() const
{
	return *this / getDistance();
}

template <typename value_type>
inline value_type getNorm(const Vec2<value_type> &tar)
{
	return tar.getNorm();
}
template <typename value_type>
inline value_type getDistance(const Vec2<value_type> &tar)
{
	return tar.getDistance();
}
template <typename value_type>
inline Vec2<value_type> fabs(const Vec2<value_type> &tar)
{
	return tar.fabs();
}
template <typename value_type>
inline Vec2<value_type> getE(const Vec2<value_type> &tar)
{
	return tar.getE();
}

template <typename value_type>
std::string to_string(const Vec2<value_type> &tar)
{
	// to_string实现得有毒，会报HEAP CORRUPTION DETECTED，沃日，主要是浮点数的有问题
	// 此处C语言可实现
	std::stringstream ss;
	ss << '(' << tar.x << " , " << tar.y << ')';
	return ss.str();
}

template <typename value_type>
std::ostream &operator<<(std::ostream &ost, const Vec2<value_type> &tar)
{
	return ost << to_string(tar);
}

template <typename value_type>
std::istream &operator>>(std::istream &ist, Vec2<value_type> &tar)
{
	return ist >> tar.x >> tar.y;
}

template <typename T>
inline bool isInRange(const T &tar, const T &lt, const T &rt)
{
	return lt <= tar && tar <= rt;
}

#endif